#include <netinet/in.h>                         // for sockaddr_in  
#include <sys/types.h>                          // for socket  
#include <sys/socket.h>                         // for socket  
#include <stdio.h>                              // for printf  
#include <stdlib.h>                             // for exit  
#include <string.h>                             // for bzero  
#include <unistd.h>
#include <arpa/inet.h>
#include <iostream>
#include <errno.h>
// #include "../file.h"
#include "../cmd.h"
using namespace std;

#define CTRL_PORT 1023
#define DATA_PORT 20
#define BUFSIZE 1024

int get_free_port()
{
    int port = 0;
    int fd = -1;
    socklen_t len= 0;
    port = -1;

    struct sockaddr_in sin;
    memset(&sin, 0, sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_port = htons(0);
    sin.sin_addr.s_addr = htonl(INADDR_ANY);
   
    fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    if(fd < 0){
        printf("socket() error\n");
         return -1;
    }
   
    if(bind(fd, (struct sockaddr *)&sin, sizeof(sin)) != 0)
    {
        printf("bind() error\n");
        close(fd);
        return -1;
    }

    len = sizeof(sin);
    if(getsockname(fd, (struct sockaddr *)&sin,(socklen_t*) &len) != 0)
    {
        printf("getsockname() error\n");
        close(fd);
        return -1;
    }

    port = sin.sin_port;
    if(fd != -1)
        close(fd);

    return port;
}

void help()
{
	printf("get filename:get a file from server\n");
	printf("put filename:put a file to server\n");
	printf("pwd:show the work path of server\n");
	printf("dir:list the files and folders in the work path of server\n");
	printf("cd:change the work path of server\n");
	printf("?:get help info\n");
	printf("quit:quit the client\n");
}

void getcmd(char* buf,Cmd* c)
{
	bzero(c,sizeof(Cmd));
	if(strncmp(buf,"pwd",3)==0)
	{
		c->cmd=PWD;
	}
	else if(strncmp(buf,"dir",3)==0)
	{
		c->cmd=LISTFILES;
	}
	else if(strncmp(buf,"?",1)==0)
	{
		c->cmd=HELP;
	}
	else if(strncmp(buf,"quit",4)==0)
	{
		c->cmd=QUIT;
	}
	else if(strncmp(buf,"cd",2)==0)
	{
		c->cmd=CWD;
		strcpy(c->name,&buf[3]);
	}
	else if(strncmp(buf,"put",3)==0)
	{
		c->cmd=PUT;
		strcpy(c->name,&buf[4]);
	}
	else if(strncmp(buf,"get",3)==0)
	{
		c->cmd=GET;
		strcpy(c->name,&buf[4]);
	}
	else
	{
		printf("unknown cmd\n");
		help();
	}
}

int pasv(int sock,Cmd* a)
{
	Cmd c;
	c.cmd=PASV;
	if(send(sock,(void*)&c,sizeof(Cmd),0)<=0)
	{
		printf("send pasv failed\n");
		return -1;
	}
	if(recv(sock,(void*)&c,sizeof(Cmd),0)<=0)
	{
		printf("recv pasv failed\n");
		return -1;
	}
	if(c.cmd==PASVSUC)
	{
		a->cmd=c.cmd;
		a->port=c.port;
		a->ip=c.ip;
		return 0;
	}
	else return -1;
}
int recvfile(char* filename,unsigned int ip,unsigned int port)
{
	int data_sock=socket(AF_INET,SOCK_STREAM,0);
	if(data_sock<0)
	{
		printf("data socket failed\n");
		return -1;
	}
	sockaddr_in data_addr;
	data_addr.sin_family=AF_INET;
	// data_addr.sin_port=htons(port);
	// data_addr.sin_addr.s_addr=htons(ip);
	data_addr.sin_port=(port);
	data_addr.sin_addr.s_addr=(ip);

	if(connect(data_sock,(sockaddr*)&data_addr,sizeof(data_addr)))
	{
		printf("data connet failed %s\n",strerror(errno));
		return -1;
	}
	FILE* fp=fopen(filename,"w");
	if(!fp)
	{
		printf("failed to open %s\n",filename);
		return -1;
	}
	char buffer[BUFSIZE];
	bzero(buffer, sizeof(buffer));  
	int length = 0;  
	while(length = recv(data_sock, buffer, BUFSIZE, 0))  
	{  
		if (length < 0)  
		{  
			printf("Recieve Data From sender Failed!\n");  
			return -1;
		}  

		int write_length = fwrite(buffer, sizeof(char), length, fp);  
		if (write_length < length)  
		{  
			printf("File:\t%s Write Failed!\n", filename);  
			return -1;
		}  
		bzero(buffer, BUFSIZE);  
	}  

	printf("Recieve File:\t %s From sender[%d] Finished!\n", filename,ip);  
	fclose(fp);
	close(data_sock);
	return 0;
}

int sendfile(char *filename,unsigned int ip,unsigned int port)
{
	int data_sock=socket(AF_INET,SOCK_STREAM,0);
	if(data_sock<0)
	{
		printf("data socket failed\n");
		return -1;
	}
	sockaddr_in data_addr;
	data_addr.sin_family=AF_INET;
	// data_addr.sin_port=htons(port);
	// data_addr.sin_addr.s_addr=htons(ip);
	data_addr.sin_port=(port);
	data_addr.sin_addr.s_addr=(ip);
	if(connect(data_sock,(sockaddr*)&data_addr,sizeof(data_addr)))
	{
		printf("data connet failed\n");
		return -1;
	}
	FILE *fp = fopen(filename, "r");  
	char buffer[BUFSIZE];
	if (fp == NULL)  
	{  
		printf("File:\t%s Not Found!\n", filename);  	
	}  
	else  
	{  
		bzero(buffer, BUFSIZE);  
		int file_block_length = 0;  
		while( (file_block_length = fread(buffer, sizeof(char), BUFSIZE, fp)) > 0)  
		{  
			printf("file_block_length = %d\n", file_block_length);  

			// 发送buffer中的字符串到new_sender_socket,实际上就是发送给客户端  
			if (send(data_sock, buffer, file_block_length, 0)!=file_block_length)  
			{  
				printf("Send File:\t%s Failed!\n", filename);  
				return -1;
			}  

			bzero(buffer, sizeof(buffer));  
		}  
		fclose(fp);  
		printf("File:\t%s Transfer Finished!\n", filename);  
	}  
	close(data_sock);
	return 0;
}


int main(int argc, char **argv)  
{  
	// sockaddr_in client_addr;
	// bzero(&client_addr,sizeof(client_addr));
	// client_addr.sin_family=AF_INET;
	// client_addr.sin_addr.s_addr=htons(INADDR_ANY);
	// client_addr.sin_port=htons(0);

	int server_socket=socket(AF_INET,SOCK_STREAM,0);
	if(server_socket<0)
	{
		printf("create socketed failed\n");
		close(server_socket);exit(1);
	}
	// if(bind(client_socket,(sockaddr*)&client_addr,sizeof(client_addr)))
	// {
	// 	printf("client bind failed\n");
	// 	close(server_socket);exit(1);
	// }
	sockaddr_in server_addr;
	bzero(&server_addr,sizeof(server_addr));

	server_addr.sin_family=AF_INET;
	server_addr.sin_port = htons(CTRL_PORT);  
	if( inet_pton(AF_INET, argv[1], &server_addr.sin_addr) <= 0){  
		printf("inet_pton error for %s\n",argv[1]);  
		exit(0);  
	} 

	socklen_t server_addr_length=sizeof(server_addr);
	if(connect(server_socket,(sockaddr*)&server_addr,server_addr_length)<0)
	{
		// printf("Can not connect to %d\n",server_addr.sin_addr.s_addr);
		printf("error:%s\n" ,strerror(errno));
		close(server_socket);exit(1);
	}
	printf("conneted to server successfully\n");
	char buf[4096];
	Cmd c;

	while(true)
	{
		printf(">>>");
		cin.getline(buf,4096);
		getcmd(buf,&c);
		// printf("cmd:%d\n",c.cmd);
		// printf("port:%d\n",c.port);
		// printf("name:%s\n",c.name);
		if(c.cmd==HELP)
		{
			help();
		}
		else if(c.cmd==QUIT)
		{
			if(send(server_socket,(void*)&c,sizeof(Cmd),0)<=0)
			{
				printf("send quit message failed\n");
				close(server_socket);exit(1);
			}
			close(server_socket);
			exit(0);
		}
		else if(c.cmd==PWD)
		{
			if(send(server_socket,(void*)&c,sizeof(Cmd),0)<=0)
			{
				printf("send pwd message failed\n");
				close(server_socket);exit(1);
			}
			if(recv(server_socket,buf,sizeof(buf),0)<=0)
			{
				printf("recv pwd failed\n");
				close(server_socket);exit(1);
			}
			printf("%s\n",buf);
		}
		else if(c.cmd==LISTFILES)
		{
			if(send(server_socket,(void*)&c,sizeof(Cmd),0)<=0)
			{
				printf("send dir message failed\n");
				close(server_socket);exit(1);
			}
			if(recv(server_socket,buf,sizeof(buf),0)<=0)
			{
				printf("recv dir failed\n");
				close(server_socket);exit(1);
			}
			printf("%s\n",buf);
		}
		else if(c.cmd==CWD)
		{
			if(send(server_socket,(void*)&c,sizeof(Cmd),0)<=0)
			{
				printf("send cd message failed\n");
				close(server_socket);exit(1);
			}
			if(recv(server_socket,buf,sizeof(buf),0)<=0)
			{
				printf("recv cd failed\n");
				close(server_socket);exit(1);
			}
			printf("%s\n",buf);
		}
		else if(c.cmd==GET)
		{
			Cmd a;
			if(pasv(server_socket,&a)==-1)
			{
				printf("pasv failed\n");
				close(server_socket);
				exit(1);
			}
			if(send(server_socket,(void*)&c,sizeof(Cmd),0)<=0)
			{
				printf("send get failed\n");
				return -1;
			}
			if(recvfile(c.name,server_addr.sin_addr.s_addr,a.port))
			{
				printf("recv %s failed\n", c.name);
				close(server_socket);
				exit(1);
			}
		}
		else if(c.cmd==PUT)
		{
			Cmd a;
			if(pasv(server_socket,&a)==-1)
			{
				printf("pasv failed\n");
				close(server_socket);
				exit(1);
			}

			if(send(server_socket,(void*)&c,sizeof(Cmd),0)<=0)
			{
				printf("send put failed\n");
				return -1;
			}
			if(sendfile(c.name,server_addr.sin_addr.s_addr,a.port))
			{
				printf("send %s failed\n", c.name);
				close(server_socket);
				exit(1);
			}
		}
		else
		{
			printf("unknown cmd, I don't know what to do\n");
			close(server_socket);exit(1);
		}
	}
	return 0;  
}  

